#load "xml-light.cma";;
#load "str.cma";;

let split_str sep str = 
  Str.split(Str.regexp_string sep) str

(*custom point type stores longitude/latitude*)
class point = 
object
  val mutable longitude = 0.000000
  val mutable latitude = 0.000000
  method get_longitude = longitude
  method get_latitude = latitude
  method set_longitude long = longitude <- long
  method set_latitude lat = latitude <- lat
end;;

(*global test point for storing elements as point type*)
let testPoint = new point

(*custom path type for roads*)
class path = 
object
  val mutable name = "null"
  val mutable pathType = "null"
  val mutable numberOfPoints = 0
  val mutable points = Array.make 100 testPoint
  method get_name = name
  method get_pathType = pathType
  method get_numberOfPoints = numberOfPoints
  method get_points = points
  method set_name n = name <- n
  method set_pathType p = pathType <- p
  method set_numberOfPoints n = numberOfPoints <- n
  method set_points p = points <- p 
end;; 

(*stores the mapObject*)
class mapObject = 
object
  val mutable name = "null"
  val mutable objectType = "null"
  val mutable speed = 0
  val mutable point = Array.make 1 testPoint
  method get_name = name
  method get_objectType = objectType
  method get_point = point
  method get_speed = speed
  method set_name n = name <- n
  method set_objectType o = objectType <- o
  method set_speed s = speed <- s
  method set_point p = point <- p
end;; 

(*custom intersection object/intersection*)
class intersection =
object 
  val mutable name = "null"
  val mutable weight = infinity
  val mutable path_one = "null"
  val mutable path_two = "null"
  val mutable point = new point
  method get_name = name
  method get_weight = weight
  method get_path_one = path_one
  method get_path_two = path_two
  method get_point = point
  method set_name n = name <- n
  method set_weight w = weight <- w
  method set_path_one p1 = path_one <- p1
  method set_path_two p2 = path_two <- p2
  method set_point lat long = point#set_latitude long; point#set_longitude lat
end;;


(*Global Variables*)
let object_num = ref 0 (*to fill the ObjectArray below*)
let object_count = ref 0 (*keeps track of how many map objecs there are *)

let intersection_num = ref 0
let intersection_count = ref 0

let path_num = ref 0
let path_count = ref 0

(*counts total number of paths*)
let increment_path_count n =
  path_count := !path_count+1;
  Printf.printf "path count %i\n" path_count.contents

let increment_mapObject_count n  =
  object_count := !object_count+1;
  Printf.printf "map object count %i\n" object_count.contents

let increment_intersection_count n =
  intersection_count := !intersection_count+1;
  Printf.printf "map object count %i\n" intersection_count.contents

(*sets a point type*)
let init_point longitude latitude = 
  let new_point = new point in
  new_point#set_longitude longitude;
  new_point#set_latitude latitude;
  Printf.printf "successfully created a new point\n" 

(*goes through the xml file first to count the number of each instance of a object type*)
let xml_parser =
  let x = Xml.parse_file "MapMeObjects.xml" in
  Xml.iter (function
  Xml.Element ("myobject", attrs, _) ->
    List.iter (function ("description", desc) -> 
      if desc = "path" then
	let name = List.assoc "name" attrs in
	increment_path_count name
      else if desc = "mapObject" then
	let name = List.assoc "name" attrs in
        increment_mapObject_count name
      else if desc = "intersection" then
        let name = List.assoc "name" attrs in
	increment_intersection_count name
      else if desc = "point\n" then
          Printf.printf "this is a point"
      else Printf.printf "not a recognizable object\n" | _ -> ()) attrs
    | _ -> ()) x

(*arrays that store the objects*)

(*map objects*)
let dummyMapObject = new mapObject (*for initializing*)
let object_named_arr = Array.make object_count.contents "null" (*name array*)
let objArray = Array.make object_count.contents dummyMapObject (*stores the actual map object*) 

(*path objects*)
let dummyPathObject = new path
let path_named_arr = Array.make path_count.contents "null"
let pathArray = Array.make path_count.contents dummyPathObject

(*intersection objects*)
let dummyintersectionObject = new intersection
let inters_named_arr = Array.make 1000 "null"
let intersArray = Array.make 1000 dummyintersectionObject

(*adds map object to the array*)
let add_mapObject name object_type point speed =
  let new_mapObject = new mapObject and
  new_point_array = Array.make 1 testPoint and
  dumb_point = new point and
  p = split_str "," point in
  (*variables := IntMap.add 4 object_num.contents !variables;*)
  new_mapObject#set_name name;
  new_mapObject#set_objectType object_type;
  new_mapObject#set_speed speed;
  (*create a point type for the object*)
  let x = float_of_string(List.nth p 0) and 
  y = float_of_string(List.nth p 1) in
  dumb_point#set_latitude x;
  dumb_point#set_longitude y;
  Array.set new_point_array 0 dumb_point;
  new_mapObject#set_point new_point_array;
  objArray.(object_num.contents) <- new_mapObject;
  object_named_arr.(object_num.contents) <- name;
  object_num := !object_num+1;
  Printf.printf "added map object:%s to array\n" new_mapObject#get_name

(*adds path object to the array*)
let add_path name path_type number_of_points points =
  (*init path array to set in the path object*)
  let new_point_array = Array.make number_of_points testPoint and
  new_path = new path in 
  new_path#set_name name;
  new_path#set_pathType path_type;
  new_path#set_numberOfPoints number_of_points;
  (*retrieve the list of points seperated by colons first*)
  let first_parse = split_str ";" points in
  (*let second_parse = split_str "," first_parse in*)
  for i = 0  to (new_path#get_numberOfPoints - 1) do
    let dumb_point = new point in
    let z1 = List.nth first_parse i in
    let x1 = split_str "," z1 in
    let x2 = float_of_string(List.nth x1 0) and
      y1 = float_of_string(List.nth x1 1) in
      dumb_point#set_latitude x2;
      dumb_point#set_longitude y1;
      Array.set new_point_array i dumb_point;
       Printf.printf "index: %i Added (%f, %f)\n" i new_point_array.(i)#get_latitude new_point_array.(i)#get_longitude;
  done;
 Printf.printf " Added (%f, %f)\n" new_point_array.(0)#get_latitude new_point_array.(0)#get_longitude;
  new_path#set_points new_point_array;
 

  (*Printf.printf "first list %s \n second list %s";*)
  pathArray.(path_num.contents) <- new_path;
  path_named_arr.(path_num.contents) <- name;
  path_num := !path_num+1;
  Printf.printf "added path object:%s to array\n" new_path#get_name

(*adds intersection object to the array*)
let add_intersection name weight path_1 path_2 x1 y1 =
  let new_intersection = new intersection in
  new_intersection#set_name name;
  new_intersection#set_weight weight;
  new_intersection#set_path_one path_1;
  new_intersection#set_path_two path_2;
  new_intersection#set_point x1 y1;
  intersArray.(intersection_num.contents) <- new_intersection;
  inters_named_arr.(intersection_num.contents) <- name;
  intersection_num := !intersection_num+1;
  Printf.printf "added intersection object:%s to array\n" new_intersection#get_name

(*now that we know how many instances of each object is in the xml, we can go through the file again and add the object to the initialized array*)
let xml_parser_2 =
  let x = Xml.parse_file "MapMeObjects.xml" in
  Xml.iter (function
  Xml.Element ("myobject", attrs, _) ->
    List.iter (function ("description", desc) -> 
      if desc = "path" then
	let name = List.assoc "name" attrs and
            p_type = List.assoc "type" attrs and 
	    n_points = int_of_string(List.assoc "numOfPoints" attrs) and
	    points = List.assoc "points" attrs in
	add_path name p_type n_points points
      else if desc = "mapObject" then
	let name = List.assoc "name" attrs and
	    o_type = List.assoc "type" attrs and
	    point = List.assoc "point" attrs and
	    speed = int_of_string(List.assoc "speed" attrs) in
	add_mapObject name o_type point speed
      else if desc = "intersection" then
	let name = List.assoc "name" attrs and
	    weight = float_of_string(List.assoc "weight" attrs) and
	    point = float_of_string(List.assoc "point" attrs) and
	    point_2 = float_of_string(List.assoc "point" attrs) and
	    path_1 = List.assoc "path1" attrs and
	    path_2 = List.assoc "path2" attrs in
	add_intersection name weight path_1 path_2 point point_2
      else if desc = "point\n" then
          Printf.printf "this is a point"
      else Printf.printf "not a recognizable object\n" | _ -> ()) attrs
    | _ -> ()) x

(*below are methods used to find the index of the searched array *)
let find_index_objectArray name = 
  let rec loop i = 
    if i >= Array.length  object_named_arr then raise Not_found 
    else if  object_named_arr.(i) = name then i
    else loop(i+1) in 
loop 0

let find_index_pathArray name = 
  let rec loop i = 
    if i >= Array.length  path_named_arr then raise Not_found 
    else if  path_named_arr.(i) = name then i
    else loop(i+1) in 
loop 0

(*retrieve the total amount of objects in the mapObjects array*)
(*arg should be either the string: mapObject or path*)
let get_num_of_objects object_name = 
  if object_name = "mapObject" then
    Array.length objArray
  else if object_name = "path" then
    Array.length pathArray
  else
    raise Not_found

(*find intersection given two string path names*)
let find_intersection p1_name p2_name =
  let index_p1 = find_index_pathArray p1_name and
  index_p2 = find_index_pathArray p2_name and
  intersection_object = new intersection in
  let p1 = pathArray.(index_p1) and
      p2 = pathArray.(index_p2) in
  (*find the index of the path*)
  let intersection_point = new point in
  let x0_1 = p1#get_points.(0)#get_longitude and
  y0_1 = p1#get_points.(0)#get_latitude and
  x1_1 = p1#get_points.((Array.length p1#get_points) - 1)#get_longitude and
  y1_1 = p1#get_points.((Array.length p1#get_points) - 1)#get_latitude and
  x0_2 = p2#get_points.(0)#get_longitude and
  y0_2 = p2#get_points.(0)#get_latitude and
  x1_2 = p2#get_points.((Array.length p2#get_points) - 1)#get_longitude and
  y1_2 = p2#get_points.((Array.length p2#get_points) - 1)#get_latitude in
  if ((y0_1 <= y0_2 && y0_2 < y1_1) && ((x0_1 -. 0.001000 <= x0_2 && x0_2 < x0_1 +. 0.001000) || (x0_1 -. 0.001000 <= x1_2 && x1_2 < x0_1 +. 0.001000))) then begin
    intersection_point#set_longitude x0_1;
    intersection_point#set_latitude y0_2;
    Printf.printf "intersection at (%f, %f)\n" y0_2 x0_1;
    let string_name = p1_name^"_"^p2_name in
    intersection_object#set_name string_name;
    Printf.printf "intersection name: %s\n" string_name;
    intersection_object#set_weight 0.0;
    intersection_object#set_path_one p1_name;
    intersection_object#set_path_two p2_name;
    intersection_object#set_point y0_2 x0_1;
    intersArray.(intersection_num.contents) <- intersection_object;
    inters_named_arr.(intersection_num.contents) <- string_name;
    intersection_num := !intersection_num+1;
    Printf.printf "added intersection object: %s to array\n" inters_named_arr.(intersection_num.contents -1); 
    string_name
  end
  else begin
    let s = "none" in
    intersection_point#set_longitude 10000.000000;
    intersection_point#set_latitude 10000.000000;
    Printf.printf "NO INTERSECTION\n";
    s
  end

(*let q = pathArray.(0)#get_name
let w = pathArray.(2)#get_name
let ip = find_intersection q w
let q = pathArray.(0)#get_name
let w = pathArray.(4)#get_name
let ip = find_intersection q w*)
